﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BusinessLogic;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Runtime.Remoting.Messaging;

namespace ChatService
{
    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class ChatService : IChat
    {
        private static Object syncObj = new Object();

        IChatCallback callback = null;

        public delegate void ChatEventHandler(object sender, ChatEventArgs e);
        public static event ChatEventHandler ChatEvent;

        private ChatEventHandler myEventHandler = null;

        static Dictionary<User, ChatEventHandler> chatters = new Dictionary<User, ChatEventHandler>();

        private User user;

        private bool checkIfPersonExists(string name)
        {
            foreach (User p in chatters.Keys)
            {
                if (p.Login.Equals(name, StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }
            }
            return false;
        }

        private ChatEventHandler getUserHandler(string name)
        {
            foreach (User p in chatters.Keys)
            {
                if (p.Login.Equals(name, StringComparison.OrdinalIgnoreCase))
                {
                    ChatEventHandler chatTo = null;
                    chatters.TryGetValue(p, out chatTo);
                    return chatTo;
                }
            }
            return null;
        }

        private User getUser(string name)
        {
            foreach (User p in chatters.Keys)
            {
                if (p.Login.Equals(name, StringComparison.OrdinalIgnoreCase))
                {
                    return p;
                }
            }
            return null;
        }

        public User[] Join()
        {
            bool userAdded = false;
            myEventHandler = new ChatEventHandler(MyEventHandler);

            lock (syncObj)
            {
                if (!checkIfPersonExists(user.Login) && user != null)
                {
                    this.user = user;
                    chatters.Add(user, MyEventHandler);
                    userAdded = true;
                }
            }

            if (userAdded)
            {
                callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
                var e = new ChatEventArgs(this.user, MessageType.UserEnter);
                BroadcastMessage(e);

                ChatEvent += myEventHandler;
                User[] list = new User[chatters.Count];
                lock (syncObj)
                {
                    chatters.Keys.CopyTo(list, 0);
                }
                return list;
            }
            else
            {
                return null;
            }
        }

        public void Say(string msg)
        {
            BroadcastMessage(new ChatEventArgs(user, msg, MessageType.Receive));
        }

        public void Whisper(string to, string msg)
        {
            try
            {
                ChatEventHandler chatterTo;

                lock (syncObj)
                {
                    chatterTo = getUserHandler(to);
                    if (chatterTo == null)
                    {
                        throw new KeyNotFoundException("Чувака з іменем " + to + " немає");
                    }
                }

                chatterTo.BeginInvoke(this, new ChatEventArgs(user, msg, MessageType.ReceiveWhisper), new AsyncCallback(EndAsync), null);
            }
            catch (KeyNotFoundException)
            {
            }
        }

        public void Leave()
        {
            if (this.user == null)
                return;

            var chatterToRemove = getUserHandler(this.user.Login);

            lock (syncObj)
            {
                chatters.Remove(this.user);
            }

            ChatEvent -= chatterToRemove;
            var e = new ChatEventArgs(user, MessageType.UserLeave);
            this.user = null;

            BroadcastMessage(e);
        }

        private void MyEventHandler(object sender, ChatEventArgs e)
        {
            try
            {
                switch (e.msgType)
                {
                    case MessageType.Receive:
                        callback.Receive(e.user, e.message);
                        break;
                    case MessageType.ReceiveWhisper:
                        callback.ReceiveWhisper(e.user, e.message);
                        break;
                    case MessageType.UserEnter:
                        callback.UserEnter(e.user);
                        break;
                    case MessageType.UserLeave:
                        callback.UserLeave(e.user);
                        break;
                }
            }
            catch
            {
                Leave();
            }
        }

        private void BroadcastMessage(ChatEventArgs e)
        {
            var temp = ChatEvent;

            if (temp != null)
            {
                foreach (ChatEventHandler handler in temp.GetInvocationList())
                {
                    handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
                }
            }
        }

        private void EndAsync(IAsyncResult ar)
        {
            ChatEventHandler chatEvent = null;

            try
            {
                AsyncResult asres = ar as AsyncResult;
                chatEvent = ((ChatEventHandler)asres.AsyncDelegate);
                chatEvent.EndInvoke(ar);
            }
            catch
            {
                ChatEvent -= chatEvent;
            }
        }
    }
}
